// @Author     : bigbeard
// @Modify by  :
// @Date       : 2021/10/26
// @Modify date:
// @Version    : 1.0
// @Description: 处理串口相关的功能,打开串口,收发数据

package comport

import (
	"fmt"
	"giot/pkg/logger"
	"giot/pkg/util"
	"time"

	"github.com/tarm/serial"
)

// Parity 校验
type Parity byte

const (
	//ParityNone None
	ParityNone Parity = 'N'
	//ParityOdd Odd
	ParityOdd Parity = 'O'
	//ParityEven Even
	ParityEven Parity = 'E'
)

type myComPort struct {
	ComName  string
	BaudRate int    `default:"9600"`
	DataBits int    `default:"8"`
	StopBits byte   `default:"1"`
	DataSize byte   `default:"8"`
	Parity   Parity `default:"ParityNone"`

	com     *serial.Port
	running bool

	dataCallback func([]byte)
}

// Open 打开串口
func (c *myComPort) Open() error {
	if c.running == true {
		return fmt.Errorf("串口模块%v正在运行中", c.ComName)
	}
	cfg := &serial.Config{Name: c.ComName, Baud: c.BaudRate}
	cfg.Size = serial.DefaultSize
	if c.DataSize != 0 {
		cfg.Size = c.DataSize
	}
	cfg.Parity = serial.ParityNone
	if c.Parity == ParityEven {
		cfg.Parity = serial.ParityEven
	} else if c.Parity == ParityOdd {
		cfg.Parity = serial.ParityOdd
	} else if c.Parity == ParityNone {
		cfg.Parity = serial.ParityNone
	} else {
		return fmt.Errorf("串口奇偶校验配置错误")
	}

	cfg.StopBits = serial.Stop1
	if c.StopBits == 2 {
		cfg.StopBits = serial.Stop2
	} else if c.StopBits == 15 {
		cfg.StopBits = serial.Stop1Half
	}
	s, e := serial.OpenPort(cfg)
	if e != nil {
		return fmt.Errorf("打开串口[%v]错误,错误信息:%v", cfg.Name, e)
	}
	c.com = s
	c.running = true
	go c.readData()
	return nil
}

// Close c port
func (c *myComPort) Close() {
	c.running = false
	if nil != c.com {
		_ = c.com.Close()
		c.com = nil
	}
}

// OnDataArrival 数据到达
func (c *myComPort) OnDataArrival(ff func([]byte)) {
	c.dataCallback = ff
}

// Write date to "com" device
func (c *myComPort) Write(b []byte) (int, error) {
	if c.running == false || c.com == nil {
		return 0, fmt.Errorf("串口设备未打开")
	}
	n, err := c.com.Write(b)
	return n, err
}

func (c *myComPort) readData() {
	defer func() {
		e := recover()
		if nil != e {
			c.Close()
		}
	}()
	if !c.running {
		return
	}
	buf := make([]byte, 1024)
	for c.running {
		time.Sleep(100 * time.Millisecond)
		n, err := c.com.Read(buf)
		if nil == err {
			logger.Debugf("从[%v]读取到>>%v", c.ComName, util.ByteToHexStr(true, buf[:n]))
			if c.dataCallback != nil {
				c.dataCallback(buf[:n])
			}
		} else {
			logger.Errorf("读取数据发生错误，错误:%v", err)
		}
	}
}
